// VC1EncASF.cpp : Implementation of CVC1EncASF

#include "stdafx.h"
#include "VC1EncASF.h"

// CVC1EncASF

CVC1EncASF::CVC1EncASF()
{
	m_dwBframeDeltaQP = 0;
    m_bClosedEntryPt = FALSE;
    m_dwDisplayHeight = 0;
    m_dwDisplayWidth = 0;
    m_dwEncodeHeight = 0; 
    m_dwEncodeWidth = 0; 
    m_bLBPresent = FALSE;
    m_bLookahead = 0;
    m_bAdaptiveGOP = TRUE;
    m_dwKeyPop = 0;
    m_dwNumThreads = 0; 
    m_dwAffinityMask = 0;
    m_dwPixHeight = 0;
    m_dwPixWidth = 0;
    m_dwPixIndex = 0;
    m_dwVideoType = 0; 
    m_bTFF = FALSE;
    m_bRFF = FALSE;
	m_StatusInt = -1;

    m_pVC1Data = NULL; 
    m_dwVC1DataSize = 0;
    m_dwOutputBufferSize = 0;
    m_qwOutputSampleTime = 0;
    m_FrameType = (FrameType)0;
    m_bColorFormat = FALSE;
    m_bPARI = FALSE;
  
    m_pFileReader = NULL;
    m_pFileWriter = NULL;
    m_pEncoder = NULL;
    m_pBIH = NULL;
    m_pWFX = NULL;
    m_ppArgv = NULL; 
    m_iArgc = 0;

    ZeroMemory(m_wszInputFilePath, MAX_PATH);
    ZeroMemory(m_wszOutputFilePath, MAX_PATH);
    ZeroMemory(m_wszProfileFilePath, MAX_PATH);
    ZeroMemory(&m_ColorFormat, sizeof(COLOR_FORMAT));
    ZeroMemory(&m_ConfigProps, sizeof(CONFIG_PROPERTIES));
    ZeroMemory(&m_Filters, sizeof(FILTERS));
    ZeroMemory(&m_MEProps, sizeof(ME_PROPERTIES));
    ZeroMemory(&m_QuantProps, sizeof(QUANT_PROPERTIES));
}

STDMETHODIMP CVC1EncASF::Init(CONFIG_PROPERTIES2 cfProperties, FILTERS2 filters, ME_PROPERTIES2 meProperties, QUANT_PROPERTIES2 qtProperties, BOOL bLookAhead, INT32 iVideoType, BSTR bsInputAVSFile, BSTR bsProfileFileName, BSTR bsOutputFileName)
{
	LOG(L"Start of Init");
    DWORD dwError = ERR_OK;
    HRESULT hr = S_OK;

	m_StatusInt = -1;

    // Create the VC-1 encoder class.
    m_pEncoder = new CVC1Encoder;

	if(dwError != ERR_OK)
	{
		LOG(L"Failure in CVC1EncASF::Init hr = %x VC1 SDK Error = %d", hr, dwError);
		return dwError;
	}

    if(NULL == m_pEncoder)
    {
        dwError = ERR_MEMORY;
    }

	m_ConfigProps = CopyToConfigProperties(cfProperties);

	wcscpy_s(m_wszOutputFilePath, MAX_PATH, bsOutputFileName);
    wcscpy_s(m_wszProfileFilePath, MAX_PATH, bsProfileFileName);
	wcscpy_s(m_wszInputFilePath, MAX_PATH, bsInputAVSFile);

	//m_pBIH = (BITMAPINFOHEADER*)bitInfoHeader;

	//m_ConfigProps = configProps;

    // Get the user's required settings.
    /*if(ERR_OK == dwError)
    {
        if(FALSE == get_userConfigProps()) 
        {
            dwError = ERR_INVALIDARG;
        }
    }*/

    // Convert to numerator, denominator.
    BOOL bResult = GetFrameRateND(m_ConfigProps.dFrameRate,
                               &m_dFRNumerator,
                               &m_dFRDenominator);
    if(FALSE == bResult)
    {
		LOG(L"ERROR: Invalid framerate %d.",m_ConfigProps.dFrameRate);
		return ERR_INVALIDSETTING;
    }  

    if(ERR_OK == dwError)
    {
		hr = InitFileReader();
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
		hr = m_pFileReader->GetFormat_Video(&m_pBIH);
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
		hr = m_pFileReader->GetFormat_Audio(&m_pWFX);
    }

    // Set the width and height based on the AVI reader stats.
    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
        m_ConfigProps.dwMaxWidth = m_pBIH->biWidth;
        m_ConfigProps.dwMaxHeight = m_pBIH->biHeight;
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
        // Configure the encoder with the user settings.
        dwError = m_pEncoder->Configure(m_ConfigProps);
    }

//  if(SUCCEEDED(hr) &&
//      ERR_OK == dwError)
//  {
//		dwError = m_pEncoder->SetLookAhead(TRUE);
//	}

    if(SUCCEEDED(hr) &&
		ERR_OK == dwError)
    {
        dwError = m_pEncoder->SetOutputMode(RAW);
    }

    // Retrieve the advanced props from the encoder.
    // Because Configure was called,
    // the advanced props should already be set appropriate defaults.
    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
		dwError = get_advancedProps();
    }

	//manually set this variable
    m_bLookahead = bLookAhead;

	//load our custom filters in
	m_Filters = CopyToFilters(filters);
	m_MEProps = CopyToMEProperties(meProperties);
	m_QuantProps = CopyToQuantProperties(qtProperties);
	m_dwVideoType = iVideoType;

	// TODO Get Advanced Properties

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
        // Set the advanced properties.
        dwError = set_advancedProps();
    }	

	//return 0;
    //if(SUCCEEDED(hr))
    //{
    //    //m_pFileReader->SeekFirst();

    //    // Start.
    //    dwError = m_pEncoder->StartEncode();

    //    LOG(L"------------Start Encode-----------");
    //}

	if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
        // Retrieve the VC-1 data buffer size.
       dwError = m_pEncoder->GetMaxOutputBuffer(&m_dwOutputBufferSize);
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
        if(m_dwOutputBufferSize > 0)
        {
            // Allocate the compressed frame buffer.
            m_pVC1Data = new BYTE[m_dwOutputBufferSize];
            if(NULL == m_pVC1Data)
            {
                dwError = ERR_MEMORY;
            }
        }
        else
        {
            dwError = ERR_INVALIDSETTING;
        }
    }

    if(FAILED(hr) ||
       ERR_OK != dwError)
    {
        // Print the error message.
		LOG(L"Failure in CVC1EncASF::Init hr = %x VC1 SDK Error = %d", hr, dwError);
    }

    return dwError;
}
HRESULT CVC1EncASF::InitFileReader()
{
    HRESULT hr = S_OK;

    assert(0 != m_dFRNumerator);
    assert(0 != m_dFRDenominator);
        
    m_pFileReader = new CAVIFileReader();
    if(NULL == m_pFileReader)
    {
        hr = E_OUTOFMEMORY;
    }  
	//LOG(L"Init Frame rate: %7d / %7d", m_dFRNumerator, m_dFRDenominator);
    if(SUCCEEDED(hr))
    {
        hr = m_pFileReader->Init(m_wszInputFilePath, m_dFRNumerator, m_dFRDenominator);
    }
 
    if(FAILED(hr))
    {
        // Print the error message.
		LOG(L"Failure in CVC1EncASF::InitFileReader hr = %x", hr);
    }
    return hr;
}
///////////////////////////////////////////////////////////////////
//
//  CVC1EncASF::Analyze
//
//  Description: Performs the 1st encoding pass.
// 
///////////////////////////////////////////////////////////////////
DWORD CVC1EncASF::Analyze(INT32 functionPointer)
{
    if(NULL == m_pEncoder ||
       NULL == m_pFileReader)
    {
        return ERR_POINTER;
    }

	int* actualFunctionPointer = (int*)functionPointer;
	BOOL (__stdcall *progresscallback)(int) = (BOOL(__stdcall*)(int))actualFunctionPointer;

    // Don't analyze for 1-pass modes.
    if(m_ConfigProps.dwRateControlMode < 2)
    {
        return ERR_OK;
    }

    HRESULT hr = S_OK;
    DWORD dwError = ERR_OK;
    DWORD dwNumBytesRead = 0;
    BYTE *pVideoData = NULL;
    DWORD dwVideoDataSize = 0;
    INT32 iTotalFrames =  0;

    // Get frame count.       
    hr =  m_pFileReader->GetFrameCount(&iTotalFrames);

    if(SUCCEEDED(hr))
    {
        m_pFileReader->SeekFirst();

        // Start.  
        dwError = m_pEncoder->StartAnalyze();
		progresscallback(201);
        //LOG(L"------------Start Analysis-----------");
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
        // Iterate frame-by-frame.
        for (INT32 iFrame = 0; iFrame < iTotalFrames; iFrame++)
        {
            INT64 tTimeStampIn = 0;
               
            hr = m_pFileReader->ReadFrame_Video(&pVideoData, &dwVideoDataSize, &tTimeStampIn);          

            if(SUCCEEDED(hr))
            {
                //LOG(L"------------Frame %7d-----------\r", iFrame);
				//only callback every tenth frame or at the end
				if( (iFrame % 10 == 0) || iFrame == iTotalFrames)
				{
    				double d = (iFrame*1.0/iTotalFrames) * 100.0;
					m_StatusInt = (INT32)int(d);
					if(progresscallback(m_StatusInt) == FALSE)
					{
						break;
					}
				}
                dwError = m_pEncoder->AnalyzeFrame (pVideoData);

                if (ERR_OK != dwError)
                {
                    break;
                }
            }
            else
            {
                // ReadFrame_Video failed for some reason.
                break;
            }
        }
    }

    if(ERR_OK == dwError &&
       SUCCEEDED(hr))
    { 
        // Wrap up.   
        dwError = m_pEncoder->EndAnalyze();
		progresscallback(202);
        LOG(L"------------End Analysis-----------"); 
    }

    if(ERR_OK == dwError &&
       SUCCEEDED(hr))
    { 
        // Display the statistics.
        dwError = ShowStatistics();
    }

    if(FAILED(hr) ||
       ERR_OK != dwError)
    {
        // Print the error message.
		LOG(L"Failure in Analyze hr = %x VC1 SDK Error = %i", hr, dwError);
    }   

    return dwError; 
}


STDMETHODIMP CVC1EncASF::Encode(INT32 functionPointer)
{
	LOG(L"Start of Encode Function");

    if(NULL == m_pEncoder ||
       NULL == m_pFileReader)
    {
        return ERR_POINTER;
    }

	m_StatusInt = 0;

    HRESULT hr = S_OK;
    DWORD dwError = ERR_OK;
    DWORD dwNumBytesRead = 0;
    BYTE *pVideoData = NULL;
    DWORD dwVideoDataSize = 0;
    INT64 tTimeStampOut = 0;
    INT32 iTotalFrames =  0;

	int* actualFunctionPointer = (int*)functionPointer;
	BOOL (__stdcall *progresscallback)(int) = (BOOL(__stdcall*)(int))actualFunctionPointer;

	//PROGRESS_CALLBACK progCall = (PROGRESS_CALLACK)actualFunctionPointer;

	//void (_stdcall *func)(int) 
	
	//attempt to Analyze first
	//LOG(L"Before analyze");
	dwError = Analyze(functionPointer);
	//LOG(L"After Analyze");

	// Get frame count.       
    hr =  m_pFileReader->GetFrameCount(&iTotalFrames);
	//LOG(L"after get frame count");

	if(SUCCEEDED(hr))
    {
        m_pFileReader->SeekFirst();

        // Start.
        dwError = m_pEncoder->StartEncode();
		//m_StatusInt = 201;
		progresscallback(203);
        LOG(L"------------Start Encode-----------");
    }

    if(SUCCEEDED(hr) && ERR_OK == dwError )
    {

		// Initialize the ASF writer only for the encoding phase.
		hr = InitFileWriter();
    } 

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
        // Iterate frame-by-frame.
        for (INT32 iFrame = 0; iFrame < iTotalFrames; iFrame++)
        {
            INT64 tTimeStampIn = 0;
               
            hr = m_pFileReader->ReadFrame_Video(&pVideoData, &dwVideoDataSize, &tTimeStampIn);          

            if(SUCCEEDED(hr))
            {
				//only callback every tenth frame or at the end
				if( (iFrame % 10 == 0) || iFrame == iTotalFrames)
				{
					double d = (iFrame*1.0/iTotalFrames) * 100.0;
					//a funny status code issue which causes the first frame (0) to
					//be reported as 100% for a split second - so just push up to start at 1 percent
					if(d < 1)
						d = 1.0;
					m_StatusInt = (INT32)int(d) + 100;
					//will return a cancel code - to tell the encoding to exit
					if(progresscallback(m_StatusInt) == FALSE)
					{
						break;
					}
				}
                //LOG(L"------------Frame %7d-----------\r", iFrame);
            }
            else
            {
                // ReadFrame_Video failed.
                break;
            }

            if(SUCCEEDED(hr))
            {
                ZeroMemory(m_pVC1Data, m_dwOutputBufferSize);
                dwError = m_pEncoder->EncodeFrame (pVideoData, m_pVC1Data, &m_dwVC1DataSize, tTimeStampIn, 
                                           &tTimeStampOut, m_bTFF, m_bRFF, &m_FrameType);

                if(ERR_OK == dwError)
                {
					dwError = WriteAudioVideo(m_pVC1Data, m_dwVC1DataSize, tTimeStampOut, m_FrameType);
                }
                else if(dwError == ERR_NO_OP_FRAME)
                {
                    // Don't write for no-op frames, just move on.
					dwError = ERR_OK;
                    continue;
                }
                else
                {
                    // Encode frame error.
                    break;
                }
            }
        } // for iFrame
    }

	if(ERR_OK == dwError &&
       SUCCEEDED(hr)) 
    {
        dwError = FlushVideo();
    }

    if (SUCCEEDED(hr) &&
        ERR_OK == dwError)
    {
        hr = FlushAudio();
    }

    if(ERR_OK == dwError &&
       SUCCEEDED(hr))
    { 
        // Wrap up.     
        dwError = m_pEncoder->EndEncode();
		progresscallback(204);
        LOG(L"------------End Encode-----------");
        //LOG(L"------------Done-----------");
    }

    if(ERR_OK == dwError &&
       SUCCEEDED(hr))
    {
        // Display the statistics.
        dwError = ShowStatistics();
    }        

    // Close file writer.
    DestroyFileWriter();

	// Nick
	//if ( m_hRawFileHandle != INVALID_HANDLE_VALUE )
	//{
	//	CloseHandle(m_hRawFileHandle);
	//	m_hRawFileHandle = INVALID_HANDLE_VALUE;
	//}

    if(FAILED(hr) ||
       ERR_OK != dwError)
    {
        // Print the error message.
		LOG(L"Failure in Encode hr = %x VC1 SDK Error = %i", hr, dwError);
    }

    return dwError;   
}



CONFIG_PROPERTIES CVC1EncASF::CopyToConfigProperties(CONFIG_PROPERTIES2 inputProperties)
{
	CONFIG_PROPERTIES newProps;

	newProps.bInterlacedSource = inputProperties.bInterlacedSource;
	newProps.dBitRate = inputProperties.dBitRate;
	newProps.dFrameRate = inputProperties.dFrameRate;
	newProps.dPeakBitRate = inputProperties.dPeakBitRate;
	newProps.dwComplexityLevel = inputProperties.dwComplexityLevel;
	newProps.dwMaxHeight = inputProperties.dwMaxHeight;
	newProps.dwMaxKeyFrameDistance = inputProperties.dwMaxKeyFrameDistance;
	newProps.dwMaxWidth = inputProperties.dwMaxWidth;
	newProps.dwNumOfBFrames = inputProperties.dwNumOfBFrames;
	newProps.dwProfile = inputProperties.dwProfile;
	newProps.dwQP = inputProperties.dwQP;
	newProps.dwRateControlMode = inputProperties.dwRateControlMode;
	newProps.dwVBVBufferInBytes = inputProperties.dwVBVBufferInBytes;

	return newProps;
}
FILTERS CVC1EncASF::CopyToFilters(FILTERS2 inputFilters)
{
	FILTERS newFilters;
	
	newFilters.bDenoise = inputFilters.bDenoise;
	newFilters.bInLoop = inputFilters.bInLoop;
	newFilters.bMedian = inputFilters.bMedian;
	newFilters.bNoiseEdgeRemoval = inputFilters.bNoiseEdgeRemoval;
	newFilters.bOverlapSmoothing = inputFilters.bOverlapSmoothing;

	return newFilters;
}

QUANT_PROPERTIES CVC1EncASF::CopyToQuantProperties(QUANT_PROPERTIES2 inputProperties)
{
	QUANT_PROPERTIES newQuant;

	newQuant.dwAdaptiveQuant = inputProperties.dwAdaptiveQuant;
	newQuant.dwDQuantBStrength = inputProperties.dwDQuantBStrength;
	newQuant.dwDQuantOption = inputProperties.dwDQuantOption;
	newQuant.dwDQuantPStrength = inputProperties.dwDQuantPStrength;

	return newQuant;
}

ME_PROPERTIES CVC1EncASF::CopyToMEProperties(ME_PROPERTIES2 inputProperties)
{
	ME_PROPERTIES newProps;

	newProps.dwDeltaMVRangeIndex = inputProperties.dwDeltaMVRangeIndex;
	newProps.dwMBModeCost = inputProperties.dwMBModeCost;
	newProps.dwMotionSearchLevel = inputProperties.dwMotionSearchLevel;
	newProps.dwMotionSearchMethod = inputProperties.dwMotionSearchMethod;
	newProps.dwMVCost = inputProperties.dwMVCost;
	newProps.dwMVRange = inputProperties.dwMVRange;

	return newProps;
}

HRESULT CVC1EncASF::InitFileWriter()
{
    if(NULL == m_pEncoder)
    { 
        return E_POINTER;
    }

    HRESULT hr = S_OK;
    DWORD dwError = ERR_OK;
    BYTE *pPrivCodecData = NULL;
    DWORD dwPrivCodecDataLen = 0;
    WCHAR *pszCodecString = NULL;
    DWORD dwRateControlMode = 0;
    double dBitRate = 0;
    BOOL bIsVBR = FALSE;  
    int iBufferInMS = 0; // Buffer window in milliseconds. 
  
    // Create an instance of the file writer class.        
    m_pFileWriter = new CASFFileWriter();
    if(NULL == m_pFileWriter)
    {
        hr = E_OUTOFMEMORY;
    }

    if(SUCCEEDED(hr))
    {        
        dwRateControlMode = m_ConfigProps.dwRateControlMode;
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {  
        bIsVBR = (dwRateControlMode == 1 ||
                  dwRateControlMode == 3 ||
                  dwRateControlMode == 4);  

        // ASF writer uses bps, not kbps.
        dBitRate = m_ConfigProps.dBitRate * 1000;
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {     
        // Get the size of the private codec data.
        dwError = m_pEncoder->GetCodecPrivateData(NULL,&dwPrivCodecDataLen, NULL);
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {
        // Allocate a buffer for the private codec data.
        pPrivCodecData = new BYTE[dwPrivCodecDataLen];
        if (NULL == pPrivCodecData) 
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {   
        ZeroMemory(pPrivCodecData, dwPrivCodecDataLen);

        // Retrieve the private codec data.
        dwError = m_pEncoder->GetCodecPrivateData(pPrivCodecData, &dwPrivCodecDataLen, NULL);
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {  
        // Allocate a buffer for the codec string.
        pszCodecString = new WCHAR[CODECSTR_MAX_LEN];
        if(NULL == pszCodecString)
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {   
        // Retrieve the codec string.
        dwError = m_pEncoder->GetCodecString(pszCodecString);
     }

    if(SUCCEEDED(hr) &&
       ERR_OK == dwError)
    {        
        if (!bIsVBR && m_ConfigProps.dBitRate > 0)
        {
            // Calculate buffer window based on VBV buffer size.
            // dBitRate is in kbps, so the result is in milliseconds.
            iBufferInMS = (int)(m_ConfigProps.dwVBVBufferInBytes * 8 / m_ConfigProps.dBitRate);
        }
        else
        {
            // Let the writer select the buffer window size if VBR.
            iBufferInMS = -1;
        }

        hr = m_pFileWriter->Init(m_wszOutputFilePath, m_wszProfileFilePath, pszCodecString,
              m_pBIH, m_pWFX, m_ConfigProps.dwProfile,m_ConfigProps.dFrameRate, bIsVBR, dBitRate,
              iBufferInMS, 
              pPrivCodecData,
              dwPrivCodecDataLen,
              m_dwOutputBufferSize);
    }

    SAFE_ARRAY_DELETE(pPrivCodecData);
    SAFE_ARRAY_DELETE(pszCodecString);

    if(FAILED(hr) ||
       ERR_OK != dwError)
    {
        // Print the error message.
        LOG(L"Failure in VC1Enc::InitFileWriter hr = %x VC1 SDK Error = %d", hr, dwError);
    }

    return hr;
}



////////////////////////////////////////////////////////
//
//  CVC1EncASF::get_advancedProps
//
//  Description: Retrieves all the advanced properties.
//
////////////////////////////////////////////////////////
DWORD CVC1EncASF::get_advancedProps()
{
    DWORD dwError = ERR_OK;

    assert(NULL != m_pEncoder);

    // GetColorFormat and GetPixelAspectRatioIndex are deliberately
    // omitted.

    if(m_ConfigProps.dwProfile != 0)
    {
        dwError = m_pEncoder->GetBFrameDeltaQP(&m_dwBframeDeltaQP); 
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetClosedEntryPoint(&m_bClosedEntryPt);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetDisplayResolution(&m_dwDisplayHeight, &m_dwDisplayWidth);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetEncodeResolution(&m_dwEncodeHeight, &m_dwEncodeWidth);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetFilters(&m_Filters);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetLetterBoxPresent(&m_bLBPresent);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetLookAhead(&m_bLookahead);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetAdaptiveGOP(&m_bAdaptiveGOP);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetKeyPopReduction(&m_dwKeyPop);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetMEProperties(&m_MEProps);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetNumOfThreads(&m_dwNumThreads, &m_dwAffinityMask);
		if (m_dwNumThreads == 3) { m_dwNumThreads = 2; m_dwAffinityMask = 3; }

    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetPixelAspectRatio(&m_dwPixHeight, &m_dwPixWidth);
    } 

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetQuantizationProperties(&m_QuantProps);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->GetVideoType(&m_dwVideoType);
    }

    if(ERR_OK != dwError)
    {
        // Print the error message.
        LOG(L"Failure in CVC1EncASF::get_advancedProps error = %i", dwError);
    }

    return dwError;
}

////////////////////////////////////////////////////////
//
//  CVC1EncASF::set_advancedProps
//
//  Description: Specifies all the advanced properties.
//
////////////////////////////////////////////////////////
DWORD CVC1EncASF::set_advancedProps()
{
    DWORD dwError = ERR_OK;

    assert(NULL != m_pEncoder);

    if(m_ConfigProps.dwProfile == 2)
    {
		//TODO: only set this value if change is requested by the user
        //dwError = m_pEncoder->SetBFrameDeltaQP(m_dwBframeDeltaQP);
    }

    if(ERR_OK == dwError)
    {
        // Only set color format info if the user provided a value.
        if(TRUE == m_bColorFormat)
        {
            dwError = m_pEncoder->SetColorFormat(m_ColorFormat);
			if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetColorFormat colorFormat = %i error = %i", m_ColorFormat, dwError);
        }
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->SetClosedEntryPoint(m_bClosedEntryPt);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetClosedEntryPoint closedEntryPoint = %i error = %i", m_bClosedEntryPt, dwError);
    }

    if(ERR_OK == dwError && m_ConfigProps.dwProfile == 2)
    {
        dwError = m_pEncoder->SetDisplayResolution(m_dwDisplayHeight, m_dwDisplayWidth);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetDisplayResolution height = %i width = %i error = %i", m_dwDisplayHeight, m_dwDisplayWidth, dwError);
    }

    if(ERR_OK == dwError && m_ConfigProps.dwProfile == 2)
    {
        dwError = m_pEncoder->SetEncodeResolution(m_dwEncodeHeight, m_dwEncodeWidth);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetEncodeResolution height = %i width = %i error = %i", m_dwEncodeHeight, m_dwEncodeWidth, dwError);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->SetFilters(m_Filters);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetFilters error = %i", dwError);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->SetLetterBoxPresent(m_bLBPresent);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetLetterBoxPresent present = %i error = %i", m_bLBPresent, dwError);
    }

    if(ERR_OK == dwError && m_ConfigProps.dwProfile == 2)
    {
        dwError = m_pEncoder->SetLookAhead(m_bLookahead);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetLookAhead lookahead = %i error = %i", m_bLookahead, dwError);
    }


    if(ERR_OK == dwError)
    {        
        dwError = m_pEncoder->SetAdaptiveGOP(m_bAdaptiveGOP);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetAdaptiveGOP adaptive = %i error = %i", m_bAdaptiveGOP, dwError);
    }

    if(ERR_OK == dwError)
    {        
        dwError = m_pEncoder->SetKeyPopReduction(m_dwKeyPop);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetKeyPopReduction reduction = %i error = %i", m_dwKeyPop, dwError);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->SetMEProperties(m_MEProps);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetMEProperties error = %i", dwError);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->SetNumOfThreads(m_dwNumThreads, m_dwAffinityMask);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetNumOfThreads numThreads = %i affinityMask = %i error = %i", m_dwNumThreads, m_dwAffinityMask, dwError);
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->SetPixelAspectRatio(m_dwPixHeight, m_dwPixWidth);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetPixelAspectRatio height = %i width = %i error = %i", m_dwPixHeight, m_dwPixWidth, dwError);
    }

    if(ERR_OK == dwError)
    {  
        // Set only if the user provided a value.
        if(TRUE == m_bPARI)
        {
            dwError = m_pEncoder->SetPixelAspectRatioIndex(m_dwPixIndex);
			if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetPixelAspectRatioIndex index = %i error = %i", m_dwPixIndex, dwError);
        }
    }

    if(ERR_OK == dwError)
    {
        dwError = m_pEncoder->SetQuantizationProperties(m_QuantProps);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetQuantizationProperties error = %i", dwError);
    }

    if(ERR_OK == dwError &&
       m_ConfigProps.dwProfile == 2)
    {
        dwError = m_pEncoder->SetVideoType(m_dwVideoType);
		if (ERR_OK != dwError) LOG(L"Failure in CVC1EncASF::set_advancedProps::SetVideoType videoType = %i error = %i", m_dwVideoType, dwError);
    }
  

    if(ERR_OK != dwError)
    {
        // Print the error message.
        LOG(L"Failure in CVC1EncASF::set_advancedProps error = %i", dwError);
    }

    return dwError;
}

///////////////////////////////////////////
//
//  CVC1EncASF::Destroy
//
//  Description: Uninitializes the VC1Enc
//               class and app.
//
///////////////////////////////////////////
STDMETHODIMP CVC1EncASF::Destroy()
{
    DestroyFileReader();
    SAFE_DELETE(m_pVC1Data);
    SAFE_DELETE(m_pEncoder);

    return 0;
}
///////////////////////////////////////////
//
//  CVC1EncASF::DestroyFileReader
//
//  Description: Uninitializes the AVI file
//               reader class.
//
///////////////////////////////////////////
void CVC1EncASF::DestroyFileReader()
{
    HRESULT hr = S_OK;

    if (m_pFileReader)
    {
        hr = m_pFileReader->Destroy();
        delete m_pFileReader;
        m_pFileReader = NULL;
    }

    if(FAILED(hr))
    {
        // Print the error message.
        LOG(L"Failure in CVC1EncASF::DestroyFileReader hr = %x", hr);
    }

    return;
}
///////////////////////////////////////////
//
//  CVC1EncASF::DestroyFileWriter
//
//  Description: Uninitializes the ASF file
//               writer class.
//
///////////////////////////////////////////
void CVC1EncASF::DestroyFileWriter()
{
    HRESULT hr = S_OK;

    if (m_pFileWriter)
    {
        hr = m_pFileWriter->Destroy();
        delete m_pFileWriter;
        m_pFileWriter = NULL;
    }

    if(FAILED(hr))
    {
        // Print the error message.
        LOG(L"Failure in CVC1EncASF::DestroyFileReader hr = %x", hr);
    }

    return;
}

//////////////////////////////////////////////////////////////////////
//
//  CVC1EncASF::WriteAudioVideo
//
//  Description: Alternately reads and writes audio and video frames.
//
/////////////////////////////////////////////////////////////////////
HRESULT CVC1EncASF::WriteAudioVideo(BYTE *pVC1Data, DWORD dwVC1DataSize,
                                         INT64 tTimeStampVideo, FrameType ft)
{
    HRESULT hr = S_OK;
    INT64 tTimeStampAudio = 0;
    BYTE *pAudioData = NULL;
    DWORD dwAudioDataSize = 0;

    if(NULL == m_pFileReader ||
       NULL == m_pFileWriter)
    {
        return E_POINTER;
    }
  
    // Get a frame of audio from the reader class.
    hr = m_pFileReader->ReadFrame_Audio(&pAudioData, &dwAudioDataSize, &tTimeStampAudio);

    if (0 == dwAudioDataSize &&
        NULL == pAudioData) 
    {
        // Out of audio data. That's not an error.
        hr = S_OK; 
    }
    else if(SUCCEEDED(hr))
    {  
        hr = m_pFileWriter->WriteAudioFrame(pAudioData, dwAudioDataSize, tTimeStampAudio);
    }       
   
    if (SUCCEEDED(hr) &&
        m_dwVC1DataSize > 0)
    {
        hr = m_pFileWriter->WriteVideoFrame(pVC1Data, dwVC1DataSize, tTimeStampVideo, ft); 
    }

     if(FAILED(hr))
    {
        // Print the error message.
        LOG(L"Failure in CVC1EncASF::WriteAudioVideo hr = %x", hr);
    }

    return hr;

}



///////////////////////////////////////////////////////////////////
//
//  CVC1EncASF::FlushVideo
//
//  Description: Flush the video encoding queue.
// 
///////////////////////////////////////////////////////////////////
DWORD CVC1EncASF::FlushVideo()
{
    DWORD dwError = ERR_OK;
    INT64 tTimeStampOut = 0;

    // Flush any remaining frames.
    while(ERR_NOMORE_FRAMES != dwError)
    {
        ZeroMemory(m_pVC1Data, m_dwVC1DataSize);

        dwError = m_pEncoder->Flush(m_pVC1Data, &m_dwVC1DataSize, &tTimeStampOut, &m_FrameType);

        if((ERR_OK == dwError || ERR_NOMORE_FRAMES == dwError)
			&& (m_dwVC1DataSize > 0))
        {
          if (ERR_IO == WriteAudioVideo(m_pVC1Data, m_dwVC1DataSize, tTimeStampOut, m_FrameType))
		  {
			break;
		  }
        }
    } 

    if(ERR_NOMORE_FRAMES == dwError)
    {
        dwError = ERR_OK;
    }

     if(ERR_OK != dwError)
    {
        // Print the error message.
        LOG(L"Failure in CVC1EncASF::FlushVideo VC1 SDK Error = %d", dwError);
    }

    return dwError;
}

///////////////////////////////////////////////////////////////////
//
//  CVC1EncASF::FlushAudio
//
//  Description: Writes remaining audio data.
// 
///////////////////////////////////////////////////////////////////
HRESULT CVC1EncASF::FlushAudio()
{
    HRESULT hr = S_OK;
    BYTE *pAudioData = NULL;
    DWORD dwAudioDataSize = 0;
    INT64 tTimeStampAudio = 0;

    if(//NULL == m_pFileReader ||
       NULL == m_pFileWriter)
    {
        return ERR_POINTER;
    }

    // Flush audio.
    do 
    {
        hr = m_pFileReader->ReadFrame_Audio(&pAudioData, &dwAudioDataSize, &tTimeStampAudio);

        if(SUCCEEDED(hr) &&
           dwAudioDataSize > 0) 
        { 
            hr = m_pFileWriter->WriteAudioFrame(pAudioData, dwAudioDataSize, tTimeStampAudio);            
        }         
    }while(dwAudioDataSize > 0);

    if(FAILED(hr))
    {
        // Print the error message.
        LOG(L"Failure in CVC1EncASF::FlushAudio hr = %x", hr);
    }

    return hr;
}

////////////////////////////////////////////////////////////////////////
//
//  CVC1EncASF::GetFrameRateND
//
//  Description: Returns the numerator and denominator values for the
//               specified frame rate.
//
////////////////////////////////////////////////////////////////////////
BOOL CVC1EncASF::GetFrameRateND (double dFrameRate, double *pdFRNumerator,
                                                        double *pdFRDenominator)
{
    // Frame rate arrays are defined in VC1EncASF.h
    BOOL bFound = FALSE;

    for (int i = 0; i < 6; i++) 
    {
        if (dFrameRate == rgdFramerate[i])
        {
            *pdFRNumerator = rgdFRNumerator[i];
            *pdFRDenominator = rgdFRDenominator[i];
            bFound = TRUE;
            break;
        }
    } 

    return bFound;
}


///////////////////////////////////////////////////////////////
//
//  CVC1EncASF::get_userConfigProps
//
//  Description: Gets configuration values from the command line.
// 
///////////////////////////////////////////////////////////////
BOOL CVC1EncASF::get_userConfigProps()
{
#ifdef _DEBUG
    // Some of the secure string functions, such as _wcslwr_s,
    // pad the buffer with a fixed character during debugging. The following
    // call shuts this off for argument parsing because this padding overwrites
    // parts of the buffer we still want to use.
    size_t sizeOld = _CrtSetDebugFillThreshold(0);
#endif

    // Set up some convenient defaults for purposes of the sample.
    m_ConfigProps.dwComplexityLevel = 3; // Balance speed/quality.
    m_ConfigProps.dwRateControlMode = 3; // Peak constrained 2-pass VBR.
    m_ConfigProps.bInterlacedSource = FALSE; // Only interlaced if the flag is set.
    m_ConfigProps.dwQP = 0; // This might need to be changed if the rate control mode is changed.
    wcscpy_s(m_wszProfileFilePath, MAX_PATH, L"default"); // If no .prx specified, find a matching codec.

    // Required settings flags.
    BOOL bInfilename = FALSE;
    BOOL bOutfilename = FALSE;
    BOOL bRate = FALSE;
    BOOL bPeak = FALSE;
    BOOL bFrameRate = FALSE;
    BOOL bMaxKey = FALSE;
    BOOL bBFrames = FALSE;
    BOOL bProfileType = FALSE;
    BOOL bVBV = FALSE;

    WCHAR *wszStopString = NULL;   

    // Get the required arguments.
    for (int i = 1; i < m_iArgc; i++)
    {  
        if(sizeof(m_ppArgv[i]) > MAX_PATH)
        {
            LOG(L"ERROR: Argument %s too long.", m_ppArgv[i]);
            //PrintHelp();

            return FALSE;
        }

        // Convert text to lowercase.
        _wcslwr_s(m_ppArgv[i], MAX_PATH);            

        //////////////////////// Required arguments //////////////////////////////

        //if (!wcscmp (L"-i", m_ppArgv[i]))
        //{
        //    // Input file.
        //    i++;

        //    if (i == m_iArgc)
        //    {
        //        LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
        //        return FALSE;
        //    }

        //    bInfilename = TRUE;
        //    wcscpy_s(m_wszInputFilePath, MAX_PATH, m_ppArgv[i]);

        //    size_t iFileLen = wcslen(m_wszInputFilePath);

        //    if (_wcsicmp(&m_wszInputFilePath[iFileLen-3], L"avi") != 0)
        //    {
        //        // We don't need to fail, but we should warn the user that this is 
        //        // unexpected.
        //        LOG(L"WARNING: Unexpected file name extension for input file.");
        //        LOG(L"\tExpected .avi.");
        //    }
        //}

        if (!wcscmp (L"-o", m_ppArgv[i]) )
        {
            // Output file.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            bOutfilename = TRUE;
            wcscpy_s(m_wszOutputFilePath, MAX_PATH, m_ppArgv[i]);

            size_t iFileLen = wcslen(m_wszOutputFilePath);

            if (_wcsicmp(&m_wszOutputFilePath[iFileLen-3], L"asf") != 0 &&
                _wcsicmp(&m_wszOutputFilePath[iFileLen-3], L"wmv") != 0)
            {
                // We don't need to fail, but we should warn the user that this is
                // unexpected.
                LOG(L"WARNING: Unexpected file name extension for output file.");
                LOG(L"\tExpected .asf or .wmv.");
            }
        }

        ///////////////////////// CONFIG_PROPERTIES ////////////////////////////

        // Note that max width and max height are retrieved from the AVI file properties.
        // Note that some of these settings are required on the command line.

        else if (!wcscmp (L"-rate", m_ppArgv[i]))
        {
            // Bit rate.
            // Required.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_ConfigProps.dBitRate = wcstod(m_ppArgv[i], &wszStopString);

            bRate = TRUE;
        }

        else if (!wcscmp (L"-complexity", m_ppArgv[i]))
        {
            // Encoder complexity.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_ConfigProps.dwComplexityLevel = wcstoul(m_ppArgv[i], NULL, 10);

            if (m_ConfigProps.dwComplexityLevel > 5)
            {
                LOG(L"ERROR: Invalid complexity: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 5);
                return FALSE;
            }
        }

        else if (!wcscmp (L"-qp", m_ppArgv[i]))
        {
            // Fixed or max QP.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_ConfigProps.dwQP = wcstoul(m_ppArgv[i], NULL, 10);
        }

        else if (!wcscmp (L"-framerate", m_ppArgv[i]))
        {
            // Frame rate. 
            // Required.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            BOOL bResult = FALSE;

            // Convert the argument to a number.
            m_ConfigProps.dFrameRate =  wcstod(m_ppArgv[i], &wszStopString);

            if (m_ConfigProps.dFrameRate < 0)
            {
                LOG(L"ERROR: Invalid framerate %s.", m_ppArgv [i]);
                return FALSE;
            } 

            // Convert to numerator, denominator.
            bResult = GetFrameRateND(m_ConfigProps.dFrameRate,
                                       &m_dFRNumerator,
                                       &m_dFRDenominator);

            if (FALSE == bResult)
            {
                LOG(L"ERROR: Invalid framerate %s.", m_ppArgv [i]);
                return FALSE;
            }   

            bFrameRate = TRUE;
        }

        else if (!wcscmp (L"-interlaced", m_ppArgv[i]))
        {
            // Interlaced source video.
            m_ConfigProps.bInterlacedSource = TRUE;       
        }

        else if (!wcscmp (L"-maxkeydist", m_ppArgv[i]))
        {
            // Max key frame distance. 
            // Required.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_ConfigProps.dwMaxKeyFrameDistance =  wcstoul(m_ppArgv[i], NULL, 10); 

            bMaxKey = TRUE;
        }

        else if (!wcscmp (L"-bframes", m_ppArgv[i]))
        {
            // Number of B frames
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_ConfigProps.dwNumOfBFrames = wcstoul(m_ppArgv[i], NULL, 10);

            if(m_ConfigProps.dwNumOfBFrames > 7)
            {
                LOG(L"ERROR: Invalid number of B-frames %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 7);
                return FALSE;
            }

            bBFrames = TRUE;
        }

        else if (!wcscmp (L"-peakrate", m_ppArgv[i]))
        {
            // Peak bit rate.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_ConfigProps.dPeakBitRate = wcstod(m_ppArgv[i], &wszStopString);

            if (m_ConfigProps.dPeakBitRate < 0.0)
            {
                LOG(L"ERROR: Invalid datarate %s.", m_ppArgv [i]);
                return FALSE;
            }

            bPeak = TRUE;
        }

        else if (!wcscmp (L"-profiletype", m_ppArgv[i]))
        {
            // Simple, Main, Advanced profile
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_ConfigProps.dwProfile = wcstoul(m_ppArgv[i], NULL, 10);

            if(m_ConfigProps.dwProfile > 2)
            {
                LOG(L"ERROR: Invalid profile. %s.  0 = Simple 1= Main 2 = Advanced", m_ppArgv [i]);
                return FALSE;
            }

            bProfileType = TRUE;
        }

        else if (!wcscmp (L"-ratecontrol", m_ppArgv[i]))
        {
            // rate control mode
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_ConfigProps.dwRateControlMode  = wcstoul(m_ppArgv[i], NULL, 10);

            if(m_ConfigProps.dwRateControlMode  > 4)
            {
                LOG(L"ERROR: Invalid rate control mode. %s. 0 = 1-pass CBR  1 = 1-pass VBR fixed QP", m_ppArgv [i]);
                LOG(L"2 = 2-pass CBR  3 = 2-pass peak constrained VBR  4 = 2-pass unconstrained VBR.");
                return FALSE;
            }
        }

        else if (!wcscmp (L"-vbv", m_ppArgv[i]))
        {
            // VBV buffer size, in bytes.
            // Required.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_ConfigProps.dwVBVBufferInBytes  = wcstoul(m_ppArgv[i], NULL, 10);

            if(m_ConfigProps.dwVBVBufferInBytes  > 4294967295)
            {
                LOG(L"ERROR: Invalid number of B-frames %s. %d.", m_ppArgv [i], 4294967295);
                return FALSE;
            }

            bVBV = TRUE;
        }

        // Not looking for these on this pass, but we need to deal with them gracefully.
 
        // These take no arguments, but are legitimate commands.
        else if (!wcscmp (L"-lbpresent", m_ppArgv[i]) ||
                 !wcscmp (L"-lookahead", m_ppArgv[i]) ||
                 !wcscmp (L"-adaptivegop", m_ppArgv[i]) ||                 
                 !wcscmp (L"-tff", m_ppArgv[i]) ||
                 !wcscmp (L"-rff", m_ppArgv[i]))
        {
            // Just ignore these.
        }

        // These take 1 argument. We'll need to skip over the argument.
        else if (!wcscmp (L"-bdeltaqp", m_ppArgv[i]) ||
                 !wcscmp (L"-denoise", m_ppArgv[i]) ||
                 !wcscmp (L"-inloop", m_ppArgv[i]) ||
                 !wcscmp (L"-median", m_ppArgv[i]) ||
                 !wcscmp (L"-overlap", m_ppArgv[i])||
                 !wcscmp (L"-noiseedge", m_ppArgv[i]) ||
                 !wcscmp (L"-deltamvrange", m_ppArgv[i]) ||
                 !wcscmp (L"-motionsearchlevel", m_ppArgv[i]) ||
                 !wcscmp (L"-mesearchmethod", m_ppArgv[i])  ||
                 !wcscmp (L"-mbcost", m_ppArgv[i]) ||
                 !wcscmp (L"-mvcost", m_ppArgv[i]) ||
                 !wcscmp (L"-mvrange", m_ppArgv[i]) ||
                 !wcscmp (L"-adaptivequant", m_ppArgv[i]) ||
                 !wcscmp (L"-dquantoption", m_ppArgv[i]) ||
                 !wcscmp (L"-dquantstrength", m_ppArgv[i]) ||
                 !wcscmp (L"-closedentrypt", m_ppArgv[i]) ||
                 !wcscmp (L"-dw", m_ppArgv[i]) ||
                 !wcscmp (L"-dh", m_ppArgv[i]) ||
                 !wcscmp (L"-ew", m_ppArgv[i]) ||
                 !wcscmp (L"-eh", m_ppArgv[i]) ||                 
                 !wcscmp (L"-threads", m_ppArgv[i]) ||
                 !wcscmp (L"-affinity", m_ppArgv[i]) ||
                 !wcscmp (L"-videotype", m_ppArgv[i]) ||
                 !wcscmp (L"-keypop", m_ppArgv[i]) ||
                 !wcscmp (L"-pr", m_ppArgv[i]))
        {
            i++;
        }

        // This one might take 3 arguments.
        else if (!wcscmp (L"-colorformatflags", m_ppArgv[i]))
        {
            // Color primary.
            i++;

            // Convert the argument to a Boolean.
            BOOL btempflag = (BOOL) _wtoi(m_ppArgv[i]);

            if (btempflag) 
            {
                i += 3;
            }
        }

        // This one might take 2 arguments.
        else if (!wcscmp (L"-aspectratio", m_ppArgv[i]))
        {
            // Pixel aspect ratio.
            i++;

            // Convert the argument to a number.
            DWORD dwPixIndex = wcstoul(m_ppArgv[i], NULL, 10);

            // 15 means "Aspect width and height transmitted"
            // Update the w and h based on command line args.
            if (m_dwPixIndex == 15)
            {
                i += 2;                  
            }           
        }

        else
        {
            // Unknown option.
            LOG(L"ERROR: Unknown option %s.", m_ppArgv[i]);
      
            //PrintHelp();
            return FALSE;
        }
    }

#ifdef _DEBUG
    _CrtSetDebugFillThreshold(sizeOld);
#endif

    //Check that we have the minimum parameters needed to encode
    if(!(bInfilename && bOutfilename && bRate && bPeak &&
         bFrameRate && bMaxKey && bBFrames && bProfileType && bVBV))
    {    
        LOG(L"ERROR: You must provide all the required arguments.");
        //PrintHelp();
        return FALSE;
    }

    return TRUE;
}

///////////////////////////////////////////////////////////////
//
//  CVC1EncASF::get_userAdvancedProps
//
//  Description: Gets advanced properties from the command line.
// 
///////////////////////////////////////////////////////////////
BOOL CVC1EncASF::get_userAdvancedProps()
{
#ifdef _DEBUG
    // Some of the secure string functions, such as _wcslwr_s,
    // pad the buffer with a fixed character during debugging. The following
    // call shuts this off for argument parsing because this padding overwrites
    // parts of the buffer we still want to use.
    size_t sizeOld = _CrtSetDebugFillThreshold(0);
#endif

    WCHAR *wszStopString = NULL;    

    for (int i = 1; i < m_iArgc; i++)
    {
        if(sizeof(m_ppArgv[i]) > MAX_PATH)
        {
            LOG(L"ERROR: Argument %s too long.", m_ppArgv[i]);
            //PrintHelp();

            return FALSE;
        }

        // Convert text to lowercase.
        _wcslwr_s(m_ppArgv[i], MAX_PATH);

         ////////////////// COLOR_FORMAT ///////////////////////

        if (!wcscmp (L"-colorformatflags", m_ppArgv[i]))
        {
            // Color primary.
            i++;

            m_bColorFormat = TRUE;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide argument(s) for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a Boolean.
            m_ColorFormat.bColorFormatFlag = (BOOL) _wtoi(m_ppArgv[i]);

            if (m_ColorFormat.bColorFormatFlag) 
            {
                i++;

                if (i == m_iArgc)
                {
                    LOG(L"ERROR: You must provide argument(s) for %s.", m_ppArgv [i - 1]);
                    return FALSE;
                }
               
                // Convert the argument to a number.
                m_ColorFormat.dwColorPrimaries = wcstoul(m_ppArgv[i], NULL, 10);
                
                i++;

                if (i == m_iArgc)
                {
                    LOG(L"ERROR: You must provide argument(s) for %s.", m_ppArgv [i - 1]);
                    return FALSE;
                }

                // Convert the argument to a number.
                m_ColorFormat.dwColorTransferChar = wcstoul(m_ppArgv[i], NULL, 10);

                i++;

                if (i == m_iArgc) 
                {
                    LOG(L"ERROR: You must provide argument(s) for %s.", m_ppArgv [i - 1]);
                    return FALSE;
                }

                // Convert the argument to a number.
                m_ColorFormat.dwColorMatrixCoefficient = wcstoul(m_ppArgv[i], NULL, 10);
            }
        }

        /////////////////// FILTERS /////////////////////////

        else if (!wcscmp (L"-denoise", m_ppArgv[i]))
        {
            //Denoise pre-processing filter on or off.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_Filters.bDenoise = (BOOL)_wtoi(m_ppArgv[i]);
        }

        else if (!wcscmp (L"-inloop", m_ppArgv[i])) 
        {
            // In-loop filter on or off.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_Filters.bInLoop = (BOOL)_wtoi(m_ppArgv[i]);
        }

        else if (!wcscmp (L"-median", m_ppArgv[i]))
        {
            //Median filter on or off.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_Filters.bMedian = (BOOL)_wtoi(m_ppArgv[i]);
        }
        
        else if (!wcscmp (L"-overlap", m_ppArgv[i])) 
        {
            // Overlap smoothing filter on or off.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_Filters.bOverlapSmoothing  = (BOOL)_wtoi(m_ppArgv[i]);
        }

        else if (!wcscmp (L"-noiseedge", m_ppArgv[i]))
        {
            //Noise edge removal on or off.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_Filters.bNoiseEdgeRemoval = (BOOL)_wtoi(m_ppArgv[i]);
        }

        //////////////////  ME_PROPERTIES  ////////////////////

        else if (!wcscmp (L"-deltamvrange", m_ppArgv[i])) 
        {
            // Delta MV range index.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_MEProps.dwDeltaMVRangeIndex = wcstoul(m_ppArgv[i], NULL, 10);

            if (m_MEProps.dwDeltaMVRangeIndex  > 3)
            {
                LOG(L"ERROR: Invalid delta MV range index: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 3);
                return FALSE;
            }
        }

        else if (!wcscmp (L"-motionsearchlevel", m_ppArgv[i])) 
        {
            // Chroma search.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_MEProps.dwMotionSearchLevel = wcstoul(m_ppArgv[i], NULL, 10);

            if (m_MEProps.dwMotionSearchLevel < 0 || m_MEProps.dwMotionSearchLevel > 4)
            {
                LOG(L"ERROR: Invalid motion search level: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 4);
                return FALSE;
            }
        } 

        else if (!wcscmp (L"-mesearchmethod", m_ppArgv[i])) 
        {
            // Motion matching.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_MEProps.dwMotionSearchMethod = wcstoul(m_ppArgv[i], NULL, 10);

            if (m_MEProps.dwMotionSearchMethod > 2)
            {
                LOG(L"ERROR: Invalid search method: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 2);
                return FALSE;
            }
        }

        else if (!wcscmp (L"-mbcost", m_ppArgv[i])) 
        {
            // Macroblock cost mode.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_MEProps.dwMBModeCost =  wcstoul(m_ppArgv[i], NULL, 10);

            if (m_MEProps.dwMBModeCost > 1)
            {
                LOG(L"ERROR: Invalid m_uiMacroblockCost: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 1);
                return FALSE;
            }
        }

        else if (!wcscmp (L"-mvcost", m_ppArgv[i])) 
        {
            // Motion vector cost mode.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_MEProps.dwMVCost =  wcstoul(m_ppArgv[i], NULL, 10);

            if (m_MEProps.dwMVCost > 1)
            {
                LOG(L"ERROR: Invalid m_uiMotion Vector Cost: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 1);
                return FALSE;
            }
        }

        else if (!wcscmp (L"-mvrange", m_ppArgv[i])) 
        {
            // MV range.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_MEProps.dwMVRange = wcstoul(m_ppArgv[i], NULL, 10);

            if (m_MEProps.dwMVRange  > 4)
            {
                LOG(L"ERROR: Invalid MV range: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 4);
                return FALSE;
            }
        }

        ///////////////////////  QUANT_PROPERTIES /////////////////////

        else if (!wcscmp (L"-adaptivequant", m_ppArgv[i])) 
        {
            // Adaptive quantization.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_QuantProps.dwAdaptiveQuant = wcstoul(m_ppArgv[i], NULL, 10);

            if (m_QuantProps.dwAdaptiveQuant > 21)
            {
                LOG(L"ERROR: Invalid adaptivequant: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 21);
                return FALSE;
            }
        }

        else if (!wcscmp (L"-dquantoption", m_ppArgv[i])) 
        {
            // Perceptual encoding.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_QuantProps.dwDQuantOption = wcstoul(m_ppArgv[i], NULL, 10);

            if (m_QuantProps.dwDQuantOption > 3)
            {
                LOG(L"ERROR: Invalid dquantoption level: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 3);
                return FALSE;
            }
        }

        else if (!wcscmp (L"-dquantpstrength", m_ppArgv[i])) 
        {
            // DQuant P strength.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_QuantProps.dwDQuantPStrength = wcstoul(m_ppArgv[i], NULL, 10);

            if (m_QuantProps.dwDQuantPStrength > 2)
            {
                LOG(L"ERROR: Invalid dquantstrength: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 2);
                return FALSE;
            }
        }

        else if (!wcscmp (L"-dquantbstrength", m_ppArgv[i])) 
        {
            // DQuant B strength.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_QuantProps.dwDQuantBStrength = wcstoul(m_ppArgv[i], NULL, 10);

            if (m_QuantProps.dwDQuantBStrength > 4)
            {
                LOG(L"ERROR: Invalid dquantstrength: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 4);
                return FALSE;
            }
        }

        ////////////////////  Other advanced settings  ///////////////////////

        else if (!wcscmp (L"-bdeltaqp", m_ppArgv[i])) 
        {
            // B delta QP.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_dwBframeDeltaQP =  wcstoul(m_ppArgv[i], NULL, 10);

            if (m_dwBframeDeltaQP > 30)
            {
                LOG(L"ERROR: Invalid bdeltaqp: %s.  Minimum %d, Maximum %d.", m_ppArgv [i], 0, 30);
                return FALSE;
            }
        } 

        else if (!wcscmp (L"-pr", m_ppArgv[i]) )
        {
            // Use audio profile.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            wcscpy_s(m_wszProfileFilePath, MAX_PATH, m_ppArgv[i]);

            size_t iFileLen = wcslen(m_wszProfileFilePath);

            if (_wcsicmp(&m_wszProfileFilePath[iFileLen-3], L"prx") != 0)
            {
                // We don't need to fail, but we should warn the user that a failure
                // is probably coming.
                LOG(L"WARNING: Unexpected file name extension for profile file.");
                LOG(L"\tExpected .prx or \"default\".");
            }
        }  

        else if (!wcscmp (L"-closedentrypt", m_ppArgv[i])) 
        {
            // Closed entry point.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_bClosedEntryPt = (BOOL)_wtoi(m_ppArgv[i]);
        }

        else if (!wcscmp (L"-dw", m_ppArgv[i]))
        {
            // Display width.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            m_dwDisplayWidth = wcstoul(m_ppArgv[i], NULL, 10);
        }

        else if (!wcscmp (L"-dh", m_ppArgv[i]) )
        {
            // Display height.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            m_dwDisplayHeight = wcstoul(m_ppArgv[i], NULL, 10);
        }

        else if (!wcscmp (L"-ew", m_ppArgv[i]))
        {
            // Encoded width.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            m_dwEncodeWidth = wcstoul(m_ppArgv[i], NULL, 10);
        }

        else if (!wcscmp (L"-eh", m_ppArgv[i]) )
        {
            // Encoded height.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            m_dwEncodeHeight = wcstoul(m_ppArgv[i], NULL, 10);
        } 

        else if (!wcscmp (L"-lbpresent", m_ppArgv[i])) 
        {
            // Letterbox present.
            m_bLBPresent = TRUE;
        }

        else if (!wcscmp (L"-lookahead", m_ppArgv[i])) 
        {
            // Look ahead. 
            m_bLookahead =  TRUE;
        } 

        else if (!wcscmp (L"-adaptivegop", m_ppArgv[i])) 
        {
            // Adaptive GOP.           
            m_bAdaptiveGOP =  TRUE;
        }

        else if (!wcscmp (L"-keypop", m_ppArgv[i])) 
        {
            // Key frame pulse reduction. 
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            m_dwKeyPop = wcstoul(m_ppArgv[i], NULL, 10);

            if(m_dwKeyPop > 4)
            {
                LOG(L"ERROR: Invalid number of threads: %s. Maximum is %d.", m_ppArgv[i], 4);
                return FALSE;
            }
        } 

        else if (!wcscmp (L"-threads", m_ppArgv[i]) )
        {
            // Thread count.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            m_dwNumThreads = wcstoul(m_ppArgv[i], NULL, 10);

            if(m_dwNumThreads > 4)
            {
                LOG(L"ERROR: Invalid number of threads: %s. Maximum is %d.", m_ppArgv[i], 4);
                return FALSE;
            }
        }

        else if (!wcscmp (L"-affinity", m_ppArgv[i]) )
        {
            // Affinity mask.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            m_dwAffinityMask = wcstoul(m_ppArgv[i], NULL, 10);
        }

        else if (!wcscmp (L"-aspectratio", m_ppArgv[i]))
        {
            // Pixel aspect ratio.
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide argument(s) for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            // Convert the argument to a number.
            m_dwPixIndex = wcstoul(m_ppArgv[i], NULL, 10);

            // 15 means "Aspect width and height transmitted"
            // Update the w and h based on command line args.
            if (m_dwPixIndex == 15)
            {
                i++;

                if (i == m_iArgc)
                {
                    LOG(L"ERROR: You must provide argument(s) for %s.", m_ppArgv [i - 1]);
                    return FALSE;
                }

                m_bPARI = TRUE;

                // Convert the argument to a number.
                m_dwPixWidth = wcstoul(m_ppArgv[i], NULL, 10);

                i++;

                if (i == m_iArgc) 
                {
                    LOG(L"ERROR: You must provide argument(s) for %s.", m_ppArgv [i - 1]);
                    return FALSE;
                }

                // Convert the argument to a number.
                m_dwPixHeight = wcstoul(m_ppArgv[i], NULL, 10);                
            }           
        }

        else if (!wcscmp (L"-videotype", m_ppArgv[i]) )
        {
            // Frame detection logic
            i++;

            if (i == m_iArgc)
            {
                LOG(L"ERROR: You must provide an argument for %s.", m_ppArgv [i - 1]);
                return FALSE;
            }

            m_dwVideoType = wcstoul(m_ppArgv[i], NULL, 10);

            if(m_dwVideoType > 4)
            {
                LOG(L"ERROR: Invalid video type: %s. Maximum is %d.", m_ppArgv[i], 4);
                return FALSE;
            }
        }

        else if (!wcscmp (L"-tff", m_ppArgv[i]))
        {
            // Top field first flag.
            m_bTFF = TRUE;
        }

        else if (!wcscmp (L"-rff", m_ppArgv[i]))
        {
            // Repeat first field flag.
            m_bRFF = TRUE;
        }

        // Not looking for these on this pass, but we need to deal with them gracefully.

        // These take an argument.
        else if (!wcscmp (L"-i", m_ppArgv[i]) ||
                 !wcscmp (L"-o", m_ppArgv[i]) ||
                 !wcscmp (L"-rate", m_ppArgv[i]) ||
                 !wcscmp (L"-complexity", m_ppArgv[i]) ||
                 !wcscmp (L"-qp", m_ppArgv[i]) ||
                 !wcscmp (L"-framerate", m_ppArgv[i]) ||
                 !wcscmp (L"-maxkeydist", m_ppArgv[i]) ||
                 !wcscmp (L"-bframes", m_ppArgv[i]) ||
                 !wcscmp (L"-peakrate", m_ppArgv[i]) ||
                 !wcscmp (L"-profiletype", m_ppArgv[i]) ||
                 !wcscmp (L"-ratecontrol", m_ppArgv[i]) ||
                 !wcscmp (L"-vbv", m_ppArgv[i]))
        {
            i++;
        }

        // This one takes no arguments.
        else if (!wcscmp (L"-interlaced", m_ppArgv[i]))
        {
            // Just ignore it.            
        }

        else
        {
            // Unknown option.
            LOG(L"ERROR: Unknown option %s.", m_ppArgv[i]);
      
            //PrintHelp();
            return FALSE;
        }
    } 

#ifdef _DEBUG
    _CrtSetDebugFillThreshold(sizeOld);
#endif

    return TRUE;
}

////////////////////////////////////////////////////////
//
//  CVC1EncASF::ShowStatistics
//
//  Description: Writes a stats line to the console window.
//
////////////////////////////////////////////////////////
DWORD CVC1EncASF::ShowStatistics()
{
    DWORD dwError = ERR_OK;
    DOUBLE dAvgBitRate = 0.0;
    DOUBLE dTotalBytes = 0.0;
    DOUBLE dTotalFrames = 0.0;
    DOUBLE dCodedFrames = 0.0;

    dwError = m_pEncoder->GetWriterStatistics(&dAvgBitRate, &dTotalBytes, &dTotalFrames, &dCodedFrames);

    if(ERR_OK == dwError)
    {
        LOG(L"Average bit rate: %6.2f, total bytes processed: %10.2f.", dAvgBitRate, dTotalBytes);
        LOG(L"Total frames: %6.2f, Coded frames: %6.2f.", dTotalFrames, dCodedFrames);
    }    

    return dwError;
}
